﻿namespace ClassyBlog.DataAccess
{
    using System;
    using System.Collections.Generic;
    using System.IO;

    using DomainObjects;
    using ExtensionMethods;
    using Infrastructure;

    public class BlogRepository : IBlogRepository
    {
        private static readonly IDictionary<string, Action<Blog, string, object>>
            knownPropertySetters = BuildSetters();

        private static readonly Action<Blog, string, object>
            unknownSetter = (blog, property, value) =>
                blog.SetDynamicProperty(property, value);

        public BlogRepository(string dataDirectory, IFileSystem fileSystem)
        {
            if (string.IsNullOrWhiteSpace(dataDirectory))
            {
                throw new ArgumentException(
                    TextMessages.Repository_Ctor_Data_Directory_Blank,
                    "dataDirectory");
            }

            if (fileSystem == null)
            {
                throw new ArgumentNullException("fileSystem");
            }

            DataPath = Path.Combine(dataDirectory, "blog.yml");
            FileSystem = fileSystem;
        }

        protected string DataPath { get; private set; }

        protected IFileSystem FileSystem { get; private set; }

        public virtual Blog One()
        {
            var content = FileSystem.ReadAll(DataPath);

            var blog = new Blog();
            blog.Load(content, knownPropertySetters, unknownSetter);

            return blog;
        }

        private static IDictionary<string, Action<Blog, string, object>>
            BuildSetters()
        {
            return new Dictionary<string, Action<Blog, string, object>>
                       {
                           {
                               "Title",
                               (blog, property, value) =>
                                   {
                                       blog.Title = value.To<string>();
                                   }
                           },
                           {
                                "Tagline",
                                (blog, property, value) =>
                                    {
                                        blog.Tagline = value.To<string>();
                                    }
                           },
                           {
                                "MetaKeywords",
                                (blog, property, value) =>
                                    {
                                        blog.MetaKeywords = value.To<string>();
                                    }
                           },
                           {
                                "MetaDescription",
                                (blog, property, value) =>
                                    {
                                        blog.MetaDescription = value.To<string>();
                                    }
                           },
                           {
                                "Author",
                                (blog, property, value) =>
                                    {
                                        blog.Author = value.To<string>();
                                    }
                           },
                           {
                                "Email",
                                (blog, property, value) =>
                                    {
                                        blog.Email = value.To<string>();
                                    }
                           },
                           {
                               "PostsPerPage",
                               (blog, property, value) =>
                                                 {
                                                     int number;

                                                     if (TryConvertToInteger(
                                                         value, out number) &&
                                                         (number > 0))
                                                     {
                                                         blog.PostsPerPage = number;
                                                     }
                                                 }
                          },
                          {
                               "PagesPerPage",
                               (blog, property, value) =>
                                                 {
                                                     int number;

                                                     if (TryConvertToInteger(
                                                         value, out number) &&
                                                         (number > 0))
                                                     {
                                                         blog.PagesPerPage = number;
                                                     }
                                                 }
                           },
                           {
                                "Theme",
                                (blog, property, value) =>
                                    {
                                        blog.Theme = value.To<string>();
                                    }
                           }
                       };
        }

        private static bool TryConvertToInteger(object value, out int number)
        {
            number = 0;

            var result = (value != null) &&
                         int.TryParse(value.ToString(), out number);

            return result;
        }
    }
}